package sf.r2dbc.dao;

import io.r2dbc.spi.Connection;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import sf.common.wrapper.Page;
import sf.core.DBCascadeField;
import sf.core.DBObject;
import sf.database.dao.DBContext;
import sf.database.dialect.DBDialect;
import sf.database.jdbc.sql.PageStrategy;
import sf.database.jdbc.sql.SQLContext;
import sf.database.jdbc.sql.SQLParameter;
import sf.dsl.DeleteDSL;
import sf.dsl.InsertDSL;
import sf.dsl.UpdateDSL;
import sf.dsl.example.Example;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * DBContext 参数可以传入null
 */
public interface R2dbcSupport {

    R2dbcClient useContext(DBContext context);

    Mono<DBDialect> getDialect();

    <T> Flux<T> fluxFunc(Function<Connection, Flux<T>> action);

    <T> Mono<T> monoFunc(Function<Connection, Mono<T>> action);

    <T extends DBObject> Mono<Long> selectCount(T query);

    <T extends DBObject> Mono<Boolean> existsByPrimaryKeys(Class<T> clz, Object... keyParams);

    <T extends DBObject> Mono<Boolean> exists(T query);

    <T extends DBObject> Flux<T> selectList(T query);

    <T extends DBObject> Mono<Page<T>> selectPage(T query, long start, int limit);

    <T> Mono<Page<T>> selectPage(Class<T> clz, SQLContext context, long start, int limit);

    <T extends DBObject> Mono<T> selectByPrimaryKeys(Class<T> clz, Object... keyParams);

    <T extends DBObject> Mono<T> selectOne(T query);

    <T extends DBObject> Mono<Integer> insert(T obj);

    <T extends DBObject> Mono<Integer> insert(T obj, boolean fast, boolean useOptimisticLock);

    /**
     * 更新对象(不区分乐观锁条件)
     * @param obj 对象
     * @param <T>
     * @return 记录变更数量
     */
    <T extends DBObject> Mono<Integer> update(T obj);

    /**
     * 乐观锁(Optimistic Lock), 以特定字段的值作为限制条件,更新对象,并更新该字段的值.参考字段的Java属性名.必须设置了@Version标签<br>
     * 注意:对于时间作为乐观锁,由于数据库和java时间存储存在差异,如果需要设置更新后的值,需要再次调用OptimisticLock.setNewOptimisticLockValues查询方法设置最新的值.
     * 锁更新规则
     * <p>
     * 1.对于数字,则+1;<br>
     * 2.对于日期取当前时间戳.<br>
     * 3.对于字符串,取UUID的值.
     * </p>
     * <p>
     * 执行的sql如下:
     * <p/>
     * <code>update t_user set age=30, city="杭州", version=version+1 where name="atshow" and version=123;</code>
     * @param obj 需要更新的对象, 必须带主键,并设置过主键的值.
     * @param <T>
     * @return 若更新成功, 返回值大于0, 否则小于等于0
     */
    <T extends DBObject> Mono<Integer> updateAndSet(T obj);

    /**
     * 合并接口
     * @param obj
     * @return
     */
    Mono<Integer> merge(DBObject obj);

    <T extends DBObject> Mono<Integer> deleteByPrimaryKeys(Class<T> clz, Object... keyParams);

    /**
     * @param entity
     * @param <T>
     * @return 删除行数
     */
    <T extends DBObject> Mono<Integer> delete(T entity);

    /**
     * @param clz
     * @param keyParams
     * @param <T>
     * @return
     */
    <T extends DBObject> Mono<Integer> logicDeleteByPrimaryKeys(Class<T> clz, Object... keyParams);

    /**
     * @param obj
     * @return
     */
    Mono<Integer> logicDelete(DBObject obj);

    <T extends DBObject> Mono<Integer> truncate(Class<T> clz);

    /**
     * 批量插入
     * @param modelList 对象列表
     * @return 执行结果
     */
    Flux<Integer> batchInsert(Collection<? extends DBObject> modelList);

    /**
     * 快速插入方法
     * @param modelList 对象列表
     * @return 执行结果
     */
    Flux<Integer> batchInsertFast(Collection<? extends DBObject> modelList);

    /**
     * 更新对象,如果有乐观锁,会根据乐观锁条件更新,并会更新数据库中的乐观锁条件.但不会更新对象的乐观锁字段的值.
     * @param modelList 对象列表
     * @return 执行结果
     */
    Flux<Integer> batchUpdate(Collection<? extends DBObject> modelList);

    /**
     * 批量删除
     * @param modelList 对象列表
     * @return 执行结果
     */
    Flux<Integer> batchDelete(Collection<? extends DBObject> modelList);

    /**
     * 查找对象列表,并查询级联字段
     * @param query  查询
     * @param clz    实体类
     * @param fields 级联字段
     * @param <T>    泛型
     * @return 列表
     */
    <T extends DBObject> Flux<T> fetchCascade(T query, Class<T> clz, DBCascadeField... fields);

    /**
     * 根据一个正则表达式，获取对象所有的关联字段
     * @param obj 数据对象,不可以是Class啊!!!传对象或集合啊!!!
     * @param <T>
     * @return 更新后的数据对象本身
     * @see javax.persistence.OneToOne
     * @see javax.persistence.ManyToMany
     * @see javax.persistence.OneToMany
     */
    <T extends DBObject> Mono<T> fetchLinks(T obj);

    /**
     * 根据字段，获取对象所有的关联字段
     * @param obj    数据对象,不可以是Class啊!!!传对象或集合啊!!!
     * @param fields
     * @param <T>
     * @return 更新后的数据对象本身
     * @see javax.persistence.OneToOne
     * @see javax.persistence.ManyToMany
     * @see javax.persistence.OneToMany
     */
    <T extends DBObject> Mono<T> fetchLinks(T obj, DBCascadeField... fields);

    /**
     * @param context
     * @return
     */
    Mono<Integer> execute(SQLContext context);

    /**
     * 将对象插入数据库同时，也将指定级联字段的所有关联字段关联的对象统统插入相应的数据库
     * <p>
     * 关于关联字段更多信息，请参看 '@One' | '@Many' | '@ManyMany' 更多的描述
     * @param obj
     * @param fields 指定字段,控制力度更细,至少一个或多个 描述了什么样的关联字段将被关注。如果为 null，则表示全部的关联字段都会被插入
     * @return
     */
    Mono<Integer> insertCascade(DBObject obj, DBCascadeField... fields);

    /**
     * 仅将对象所有的关联字段插入到数据库中，并不包括对象本身
     * @param obj    数据对象
     * @param fields 字段名称，描述了什么样的关联字段将被关注。如果为 null，则表示全部的关联字段都会被插入
     * @return 数据对象本身
     * @see javax.persistence.OneToOne
     * @see javax.persistence.ManyToMany
     * @see javax.persistence.OneToMany
     */
    <T extends DBObject> Mono<T> insertLinks(T obj, DBCascadeField... fields);

    /**
     * 将对象的一个或者多个，多对多的关联信息，插入数据表
     * @param obj    对象
     * @param fields 正则表达式，描述了那种多对多关联字段将被执行该操作
     * @return 对象自身
     * @see javax.persistence.ManyToMany
     */
    <T extends DBObject> Mono<T> insertRelation(T obj, DBCascadeField... fields);

    /**
     * 将对象更新的同时，也将指定级联字段的所有关联字段关联的对象统统更新
     * <p>
     * 关于关联字段更多信息，请参看 '@One' | '@Many' | '@ManyMany' 更多的描述
     * @param obj    数据对象
     * @param fields 指定字段,控制力度更细,至少一个或多个,描述了什么样的关联字段将被关注。如果为 null，则表示全部的关联字段都会被更新
     * @return 执行结果
     */
    Mono<Integer> updateCascade(DBObject obj, DBCascadeField... fields);

    /**
     * 仅更新对象所有的关联字段，并不包括对象本身
     * @param obj    数据对象
     * @param fields 字段名称，描述了什么样的关联字段将被关注。如果为 null，则表示全部的关联字段都会被更新
     * @param <T>
     * @return 对象内部更新的级联数量
     * @see javax.persistence.OneToOne
     * @see javax.persistence.ManyToMany
     * @see javax.persistence.OneToMany
     */
    <T extends DBObject> Mono<Integer> updateLinks(T obj, DBCascadeField... fields);

    /**
     * 多对多关联是通过一个中间表将两条数据表记录关联起来。
     * <p>
     * 而这个中间表可能还有其他的字段，比如描述关联的权重等
     * <p>
     * 这个操作可以让你一次更新某一个对象中多个多对多关联的数据
     * @param obj    数据对象
     * @param fields 字段名称，描述了那种多对多关联字段将被执行该操作
     * @param <T>
     * @return 共有多少条数据被更新
     * @see javax.persistence.ManyToMany
     */
    <T extends DBObject> Mono<Integer> updateRelation(T obj, DBCascadeField... fields);

    /**
     * 将对象删除的同时，也将指定级联字段的所有关联字段关联的对象统统删除 <b style=color:red>注意：</b>
     * <p>
     * Java 对象的字段会被保留，这里的删除，将只会删除数据库中的记录
     * <p>
     * 关于关联字段更多信息，请参看 '@One' | '@Many' | '@ManyMany' 更多的描述
     * @param obj    对象
     * @param fields 指定字段,控制力度更细,至少一个或多个 描述了什么样的关联字段将被关注。如果为 null，则表示全部的关联字段都会被删除
     * @param <T>    泛型
     * @return 执行结果
     */
    <T extends DBObject> Mono<Integer> deleteCascade(T obj, DBCascadeField... fields);

    /**
     * 仅删除对象所有的关联字段，并不包括对象本身。 <b style=color:red>注意：</b>
     * <p>
     * Java 对象的字段会被保留，这里的删除，将只会删除数据库中的记录
     * <p>
     * 关于关联字段更多信息，请参看 '@One' | '@Many' | '@ManyMany' 更多的描述
     * @param obj    数据对象
     * @param fields 字段名称，描述了什么样的关联字段将被关注。如果为 null，则表示全部的关联字段都会被删除
     * @param <T>
     * @return 被影响的记录行数
     * @see javax.persistence.OneToOne
     * @see javax.persistence.ManyToOne
     * @see javax.persistence.ManyToMany
     */
    <T extends DBObject> Mono<Integer> deleteLinks(T obj, DBCascadeField... fields);

    /**
     * 多对多关联是通过一个中间表将两条数据表记录关联起来。
     * <p>
     * 而这个中间表可能还有其他的字段，比如描述关联的权重等
     * <p>
     * 这个操作可以让你一次删除某一个对象中多个多对多关联的数据
     * @param obj
     * @param fields 字段名称，描述了那种多对多关联字段将被执行该操作
     * @return 共有多少条数据被更新
     * @see javax.persistence.ManyToMany
     */
    <T extends DBObject> Mono<Integer> deleteRelation(T obj, DBCascadeField... fields);
    ////////////===================== Example使用 带参数=============//////////////

    <T> Mono<Page<T>> selectPage(long start, int limit, Class<T> beanClass, Example example);

    Mono<Object[]> selectArray(Example example);

    <T> Flux<T> selectList(Class<T> beanClass, Example example);

    <T> Flux<T> selectList(Class<T> beanClass, long start, int limit, Example example);

    <T> Flux<T> selectListForUpdate(Class<T> beanClass, Example example);

    <T> Mono<T> selectOne(Class<T> beanClass, Example example);

    <T> Mono<T> selectOneForUpdate(Class<T> beanClass, Example example);

    Flux<Map<String, Object>> select(Example example);

    /**
     * @param insertDSL
     * @param datas    主键返回信息
     * @return
     */
    Mono<Integer> insert(InsertDSL insertDSL, List<DBObject> datas);

    /**
     * @param updateDSL
     * @return
     */
    Mono<Integer> update(UpdateDSL updateDSL);

    /**
     * @param deleteDSL
     * @return
     */
    Mono<Integer> delete(DeleteDSL deleteDSL);
    ////////////===================== sql使用 带参数=============//////////////

    /**
     * 查询 map ,注意返回结果字段不区分大小写.
     * @param sql   原始语句
     * @param paras 参数
     * @return 列表
     */
    Flux<Map<String, Object>> selectList(String sql, List<SQLParameter> paras);

    /**
     * 查询单一对象
     * @param beanClass  实体类
     * @param sql        语句
     * @param parameters 参数
     * @param <T>        泛型
     * @return 单一对象
     */
    <T> Mono<T> selectOne(Class<T> beanClass, String sql, List<SQLParameter> parameters);


    /**
     * @param query
     * @param <T>
     * @return
     */
    <T extends DBObject> Mono<T> selectOneForUpdate(T query);

    /**
     * @param query
     * @param start
     * @param limit
     * @param <T>
     * @return
     */
    <T extends DBObject> Flux<T> selectList(T query, long start, int limit);

    /**
     * @param query
     * @param <T>
     * @return
     */
    <T extends DBObject> Flux<T> selectListForUpdate(T query);

    /**
     * @param beanClass
     * @param start
     * @param limit
     * @param sql
     * @param parameters
     * @param <T>
     * @return
     */
    <T> Flux<T> selectList(Class<T> beanClass, long start, int limit, String sql, List<SQLParameter> parameters);

    /**
     * 根据sql查询,返回对应的列表
     * @param beanClass  返回结果,可以为实体类,Map,List,Object[]
     * @param sql        原始语句
     * @param parameters 参数
     * @param <T>        泛型
     * @return 列表
     */
    <T> Flux<T> selectList(Class<T> beanClass, String sql, List<SQLParameter> parameters);

    <T> Mono<Page<T>> selectPage(long start, int limit, Class<T> beanClass, String sql, Object... parameters);

    <T> Mono<Page<T>> selectPageRaw(long start, int limit, Class<T> beanClass, String countSql, Object[] countParas, String listSql, Object[] listParas);

    <T> Mono<Page<T>> selectPageRaw(long start, int limit, Class<T> beanClass, String countSql, Object[] countParas, String listSql, Object[] listParas, PageStrategy strategy);

    /**
     * @param pkeys
     * @param keyValues
     * @param sql
     * @param parameters
     * @return
     */
    Mono<Integer> insert(List<String> pkeys, Map<String, Object> keyValues, String sql, List<SQLParameter> parameters);

    /**
     * @param sql        语句
     * @param parameters 参数
     * @return 1执行成功, 0未执行成功
     */
    Mono<Integer> execute(String sql, List<SQLParameter> parameters);


    ////////////===================== sql使用 原始参数=============//////////////

    /**
     * 查询 map ,注意返回结果字段不区分大小写.
     * @param sql        原始语句
     * @param parameters 参数
     * @return 列表
     */
    Flux<Map<String, Object>> selectList(String sql, Object... parameters);

    /**
     * 查询单一对象
     * @param beanClass  实体类
     * @param sql        语句
     * @param parameters 参数
     * @param <T>        泛型
     * @return 单一对象
     */
    <T> Mono<T> selectOne(Class<T> beanClass, String sql, Object... parameters);


    /**
     * @param beanClass
     * @param start
     * @param limit
     * @param sql
     * @param parameters
     * @param <T>
     * @return
     */
    <T> Flux<T> selectList(Class<T> beanClass, long start, int limit, String sql, Object... parameters);

    /**
     * 根据sql查询,返回对应的列表
     * @param beanClass  返回结果,可以为实体类,Map,List,Object[]
     * @param sql        原始语句
     * @param parameters 参数
     * @param <T>        泛型
     * @return 列表
     */
    <T> Flux<T> selectList(Class<T> beanClass, String sql, Object... parameters);

    /**
     * @param pkeys
     * @param keyValues
     * @param sql
     * @param parameters
     * @return
     */
    Mono<Integer> insert(List<String> pkeys, Map<String, Object> keyValues, String sql, Object... parameters);

    /**
     * @param sql        语句
     * @param parameters 参数
     * @return 1执行成功, 0未执行成功
     */
    Mono<Integer> execute(String sql, Object... parameters);

    /**
     * 无参数sql执行
     * @param sql 无参数语句
     * @return 1执行成功, 0未执行成功
     */
    Mono<Integer> execute(String sql);
}
